#include "PSFSteppingAction.hh"

#include "PSFDetectorConstruction.hh"
#include "PSFEventAction.hh"
#include "PSFPhotonDetSD.hh"
#include "PSFSteppingActionMessenger.hh"
#include "PSFUserTrackInformation.hh"

#include "G4ios.hh"
#include "G4OpBoundaryProcess.hh"
#include "G4OpticalPhoton.hh"
#include "G4ProcessManager.hh"
#include "G4Run.hh"
#include "G4SDManager.hh"
#include "G4Step.hh"
#include "G4StepPoint.hh"
#include "G4SystemOfUnits.hh"
#include "G4ThreeVector.hh"
#include "G4Track.hh"
#include "G4TrackStatus.hh"
#include "G4UImanager.hh"
#include "G4VPhysicalVolume.hh"
#include "G4UnitsTable.hh"

// Purpose: Save relevant information into User Track Information

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

PSFSteppingAction::PSFSteppingAction(PSFDetectorConstruction *detector,
                                     PSFEventAction *event)
    : fDetector(detector), fEventAction(event)
{
  fSteppingMessenger = new PSFSteppingActionMessenger(this);
  ResetCounters();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

PSFSteppingAction::~PSFSteppingAction() { delete fSteppingMessenger; }

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFSteppingAction::SetBounceLimit(G4int i) { fBounceLimit = i; }

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4int PSFSteppingAction::GetNumberOfBounces() { return fCounterBounce; }

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4int PSFSteppingAction::GetNumberOfClad1Bounces()
{
  return fCounterClad1Bounce;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4int PSFSteppingAction::GetNumberOfClad2Bounces()
{
  return fCounterClad2Bounce;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4int PSFSteppingAction::GetNumberOfPSFCoreBounces() { return fCounterPSFCoreBounce; }

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4int PSFSteppingAction::ResetSuccessCounter()
{
  G4int temp = fCounterEnd;
  fCounterEnd = 0;
  return temp;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFSteppingAction::UserSteppingAction(const G4Step *theStep)
{
  G4Track *theTrack = theStep->GetTrack();
  auto trackInformation =
      (PSFUserTrackInformation *)theTrack->GetUserInformation();

  G4StepPoint *thePrePoint = theStep->GetPreStepPoint();
  G4StepPoint *thePostPoint = theStep->GetPostStepPoint();

  G4VPhysicalVolume *thePrePV = thePrePoint->GetPhysicalVolume();
  G4VPhysicalVolume *thePostPV = thePostPoint->GetPhysicalVolume();

  G4String thePrePVname = " ";
  G4String thePostPVname = " ";

  if (thePostPV)
  {
    thePrePVname = thePrePV->GetName();
    thePostPVname = thePostPV->GetName();
  }

  // Recording data for start
  // static const G4ThreeVector ZHat = G4ThreeVector(0.0,0.0,1.0);
  if (theTrack->GetParentID() == 0)
  {
    // This is a primary track
    if (theTrack->GetCurrentStepNumber() == 1)
    {
      // G4double x = theTrack->GetVertexPosition().x();
      // G4double y = theTrack->GetVertexPosition().y();

      // G4double pz = theTrack->GetVertexMomentumDirection().z();
      // G4double fInitTheta =
      //     theTrack->GetVertexMomentumDirection().angle(ZHat);
    }
  }
  ////////////////////////////////////////////////////////////////////////////////////////////

  // 统计缪子在光纤芯中的能量沉积
  if (theTrack->GetParticleDefinition()->GetParticleName() == "mu+" || theTrack->GetParticleDefinition()->GetParticleName() == "mu-" ||
      theTrack->GetParticleDefinition()->GetParticleName() == "e+" || theTrack->GetParticleDefinition()->GetParticleName() == "e-")
  {
    if (thePrePVname == "PSFCore")
    {
      fEventAction->AddPEdep((theStep->GetTotalEnergyDeposit()));
    }
  }

  // 不是光子退出
  if (theTrack->GetParticleDefinition()->GetParticleName() != "opticalphoton")
  {
    return;
  }
  // 统计初始光子数
  G4double edep = theTrack->GetKineticEnergy();
  if (theTrack->GetCurrentStepNumber() == 1)
  {
    fEventAction->Addpphoton(); // 累积光子数
  }
  /////////////////////////////////////////////////////////////////////////////////////////
  // Retrieve the status of the photon
  G4OpBoundaryProcessStatus theStatus = Undefined;

  static G4ThreadLocal G4ProcessManager *OpManager =
      G4OpticalPhoton::OpticalPhoton()->GetProcessManager();

  if (OpManager)
  {
    G4int nproc = OpManager->GetPostStepProcessVector()->entries();
    G4ProcessVector *fPostStepDoItVector =
        OpManager->GetPostStepProcessVector(typeDoIt);

    for (G4int i = 0; i < nproc; ++i)
    {
      G4VProcess *fCurrentProcess = (*fPostStepDoItVector)[i];
      fOpProcess = dynamic_cast<G4OpBoundaryProcess *>(fCurrentProcess);
      if (fOpProcess)
      {
        theStatus = fOpProcess->GetStatus();
        break;
      }
    }
  }

  // 杀死光子阻止传输
  // if (theTrack->GetCurrentStepNumber() > 6)
  //   theTrack->SetTrackStatus(fKillTrackAndSecondaries);
  // 设置每个Event打印输出
  // fEventAction->SetEventVerbose(2);

  // 统计光子反射类型
  // 发射光子
  G4double stepLength = 0.;
  if (fEventAction->GetVerboseLevel() > 0)
  {
    if (theTrack->GetParentID() == 0)
    {
      if (fEventAction->GetInitGamma() == -1 && thePrePVname == "PSFCore")
      {
        if (theStatus == TotalInternalReflection)
        {
          fEventAction->AddCoreTIR();
          fEventAction->SetInitGamma();
        }
        else if (theStatus == FresnelReflection)
        {
          fEventAction->AddCoreR();
          fEventAction->SetInitGamma();
        }
      }

      if (fEventAction->GetInitGamma() == -1 && thePrePVname == "Clad1")
      {
        if (theStatus == TotalInternalReflection)
        {
          fEventAction->AddClad1TIR();
          fEventAction->SetInitGamma();
        }
        else if (theStatus == FresnelReflection)
        {
          fEventAction->AddClad1R();
          fEventAction->SetInitGamma();
        }
      }

      if (fEventAction->GetInitCoupleR() == -1 && thePrePVname == "ClearFiber" &&
          (theStatus == TotalInternalReflection || theStatus == FresnelReflection))
      {
        fEventAction->AddCoupleR();
        fEventAction->SetInitCoupleR();
      }

      if (fEventAction->GetInitDet() == -1 && thePostPVname == "PhotonDet")
      {
        fEventAction->SetInitDet();
      }

      if (fEventAction->GetInitCoupleR() == -1 && thePrePVname == "PSFCore")
      {
        stepLength = theStep->GetStepLength();
        fEventAction->AddAbsCoreL(stepLength);
      }

      if (fEventAction->GetInitCoupleR() == -1 && thePrePVname == "Clad1")
      {
        stepLength = theStep->GetStepLength();
        fEventAction->AddAbsClad1L(stepLength);
      }
    }
    else // 发射缪子
    {
      if (trackInformation->GetInitGamma() == -1 && thePrePVname == "PSFCore")
      {
        if (theStatus == TotalInternalReflection)
        {
          fEventAction->AddCoreTIR();
          trackInformation->SetInitGamma();
        }
        else if (theStatus == FresnelReflection)
        {
          fEventAction->AddCoreR();
          trackInformation->SetInitGamma();
        }
      }
      if (trackInformation->GetInitGamma() == -1 && thePrePVname == "Clad1")
      {
        if (theStatus == TotalInternalReflection)
        {
          fEventAction->AddClad1TIR();
          trackInformation->SetInitGamma();
        }
        else if (theStatus == FresnelReflection)
        {
          fEventAction->AddClad1R();
          trackInformation->SetInitGamma();
        }
      }

      if (trackInformation->GetInitCoupleR() == -1 && thePrePVname == "ClearFiber" &&
          (theStatus == TotalInternalReflection || theStatus == FresnelReflection))
      {
        fEventAction->AddCoupleR();
        trackInformation->SetInitCoupleR();
      }

      if (trackInformation->GetInitDet() == -1 && thePostPVname == "PhotonDet")
      {
        trackInformation->SetInitDet();
      }

      if (trackInformation->GetInitCoupleR() == -1 && thePrePVname == "PSFCore")
      {
        stepLength = theStep->GetStepLength();
        fEventAction->AddAbsCoreL(stepLength);
      }

      if (trackInformation->GetInitCoupleR() == -1 && thePrePVname == "Clad1")
      {
        stepLength = theStep->GetStepLength();
        fEventAction->AddAbsClad1L(stepLength);
      }
    }
  }
  // Find the skewness of the ray at first change of boundary
  if (fInitGamma == -1 &&
      (theStatus == TotalInternalReflection || theStatus == FresnelReflection ||
       theStatus == FresnelRefraction) &&
      trackInformation->IsStatus(InsideOfFiber))
  {
    G4double px = theTrack->GetVertexMomentumDirection().x();
    G4double py = theTrack->GetVertexMomentumDirection().y();
    G4double x = theTrack->GetPosition().x();
    G4double y = theTrack->GetPosition().y();

    fInitGamma = x * px + y * py;

    fInitGamma =
        fInitGamma / std::sqrt(px * px + py * py) / std::sqrt(x * x + y * y);

    fInitGamma = std::acos(fInitGamma * rad);

    if (fInitGamma / deg > 90.0)
    {
      fInitGamma = 180 * deg - fInitGamma;
    }
  }
  // Record Photons that missed the photon detector but escaped from readout
  if (!thePostPV && trackInformation->IsStatus(EscapedFromReadOut))
  {
    // G4cout << "SteppingAction: status = EscapedFromReadOut" << G4endl;
    fEventAction->AddEscaped();
    // UpdateHistogramSuccess(thePostPoint,theTrack);
    ResetCounters();

    return;
  }

  // Assumed photons are originated at the fiber OR
  // the fiber is the first material the photon hits
  switch (theStatus)
  {
  // Exiting the fiber
  case FresnelRefraction:
  case SameMaterial:
    fEventAction->AddExiting();

    if (thePostPVname == "PSFCore" || thePostPVname == "Clad1" ||
        thePostPVname == "Clad2")
    {
      if (trackInformation->IsStatus(OutsideOfFiber))
        trackInformation->AddStatusFlag(InsideOfFiber);

      // Set the Exit flag when the photon refracted out of the fiber
    }
    else if (trackInformation->IsStatus(InsideOfFiber))
    {
      // EscapedFromReadOut if the z position is the same as fiber's end
      if (theTrack->GetPosition().z() == fDetector->GetPSFCoreEnd() || theTrack->GetPosition().z() == -1 * fDetector->GetPSFCoreEnd())
      {
        trackInformation->AddStatusFlag(EscapedFromReadOut);
        fCounterEnd++;
        fEventAction->AddEscapedEnd();
      }
      else // Escaped from side
      {
        trackInformation->AddStatusFlag(EscapedFromSide);
        trackInformation->SetExitPosition(theTrack->GetPosition());
        //  UpdateHistogramEscape(thePostPoint,theTrack);

        fCounterMid++;
        fEventAction->AddEscapedMid();
        ResetCounters();
      }

      trackInformation->AddStatusFlag(OutsideOfFiber);
      trackInformation->SetExitPosition(theTrack->GetPosition());
    }

    return;

  // Internal Reflections
  case TotalInternalReflection:

    fEventAction->AddTIR();

    // Kill the track if it's number of bounces exceeded the limit
    if (fBounceLimit > 0 && fCounterBounce >= fBounceLimit)
    {
      theTrack->SetTrackStatus(fStopAndKill);
      trackInformation->AddStatusFlag(murderee);
      ResetCounters();
      G4cout << "\n Bounce Limit Exceeded" << G4endl;
      return;
    }
    break;

  case FresnelReflection:

    fCounterBounce++;
    fEventAction->AddBounce();

    if (thePrePVname == "PSFCore")
    {
      fCounterPSFCoreBounce++;
      fEventAction->AddPSFCoreBounce();
    }
    else if (thePrePVname == "Clad1")
    {
      fCounterClad1Bounce++;
      fEventAction->AddClad1Bounce();
    }
    else if (thePrePVname == "Clad2")
    {
      fCounterClad2Bounce++;
      fEventAction->AddClad1Bounce();
    }

    // Determine if the photon has reflected off the read-out end
    if (theTrack->GetPosition().z() == fDetector->GetPSFCoreEnd() || theTrack->GetPosition().z() == -1 * fDetector->GetPSFCoreEnd())
    {
      if (!trackInformation->IsStatus(ReflectedAtReadOut) &&
          trackInformation->IsStatus(InsideOfFiber))
      {
        trackInformation->AddStatusFlag(ReflectedAtReadOut);

        if (fDetector->IsPerfectFiber() &&
            theStatus == TotalInternalReflection)
        {
          theTrack->SetTrackStatus(fStopAndKill);
          trackInformation->AddStatusFlag(murderee);
          // UpdateHistogramReflect(thePostPoint,theTrack);
          ResetCounters();
          return;
        }
      }
    }
    return;

  // Reflection off the mirror
  case LambertianReflection:
  case LobeReflection:
  case SpikeReflection:

    fEventAction->AddReflected();
    // Check if it hits the mirror
    if (thePostPVname == "Mirror")
    {
      trackInformation->AddStatusFlag(ReflectedAtMirror);
      fEventAction->AddMirror();
    }
    return;

  // Detected by a detector
  case Detection:
    // Detected automatically with G4OpBoundaryProcess->InvokeSD set true

    // Stop Tracking when it hits the detector's surface
    ResetCounters();
    theTrack->SetTrackStatus(fStopAndKill);
    return;

  default:
    break;
  }

  // Check for absorbed photons
  if (theTrack->GetTrackStatus() != fAlive &&
      trackInformation->IsStatus(InsideOfFiber))
  {
    // UpdateHistogramAbsorb(thePostPoint,theTrack);
    ResetCounters();
    return;
  }
}
